//============================================================================
// Name        : tp2.cpp
// Author      : Sergio Colinas
// Version     :
// Copyright   : 
// Description : Trabajo Práctico Nro 2
//============================================================================

#include "tp2.h"

int main(int argc, char** argv) {

	Logger *log = NULL;
	bool debug = false;
	bool server = false;

	//Parseo los parámetros que recibe el programa
	if (parse_command_line(argc, argv, &server, &debug) != 0) {
		return 1;
	} else if(server) {
		//Obtengo la instancia del Logger (le paso el flag de debug)
		log = Logger::Instance(debug);
		Servidor *servidor = new Servidor("/bin/ls", 'a');
		SIGINT_Handler sigint_handler;
		//Creo 6 procesos, uno por cada cola de mensajes y otro para manejar el fin del programa
		if(fork()==0) {
			if(fork()==0) {
				if(fork()==0) {
					if(fork()==0) {
						if(fork()==0) {
							cout<<"Presione cualquier tecla para terminar"<<endl;
							log->writeToLogFile("Server P1 - Esperando que se presione una tecla para terminar\n");
							getchar();
							while(servidor->usuariosConectados()>0) {
								cout<<"Hay "<<servidor->usuariosConectados()<<" usuarios conectados al servidor, presione cualquier tecla para reintentar"<<endl;
								log->writeToLogFile("Server P1 - Hay clientes conectados, esperando que se presione una tecla para terminar\n");
								getchar();
							}
							log->writeToLogFile("Server P1 - Liberando memoria\n");
							delete(servidor);
							string tmp = "Server P1 - Enviando señal SIGINT a pid = ";
							char pid[8];
							sprintf(pid, "%d",getppid());
							tmp += pid;
							tmp += "\n";
							log->writeToLogFile(tmp);
							kill(getppid(), SIGINT);
							log->writeToLogFile("Server P1 - Terminó\n");
							exit(0);
						}
						else {
							SignalHandler :: getInstance()->registrarHandler(SIGINT, &sigint_handler);
							log->writeToLogFile("Server P2 - Atendiendo peticiones de lista de usuarios hasta recibir la señal SIGINT\n");
							while(sigint_handler.getGracefulQuit() == 0)
								servidor->atenderPeticionListaDeUsuarios();
							SignalHandler::destruir();
							log->writeToLogFile("Server P2 - Liberando memoria\n");
							delete(servidor);
							string tmp = "Server P2 - Enviando señal SIGINT a pid = ";
							char pid[8];
							sprintf(pid, "%d",getppid());
							tmp += pid;
							tmp += "\n";
							log->writeToLogFile(tmp);
							kill(getppid(), SIGINT);
							log->writeToLogFile("Server P2 - Terminó\n");
							exit(0);
						}
					}
					else {
						SignalHandler :: getInstance()->registrarHandler(SIGINT, &sigint_handler);
						log->writeToLogFile("Server P3 - Atendiendo peticiones de conexión a sala de chat hasta recibir la señal SIGINT\n");
						while(sigint_handler.getGracefulQuit() == 0)
							servidor->atenderPeticionConectarASala();
						SignalHandler::destruir();
						log->writeToLogFile("Server P3 - Liberando memoria\n");
						delete(servidor);
						string tmp = "Server P3 - Enviando señal SIGINT a pid = ";
						char pid[8];
						sprintf(pid, "%d",getppid());
						tmp += pid;
						tmp += "\n";
						log->writeToLogFile(tmp);
						kill(getppid(), SIGINT);
						log->writeToLogFile("Server P3 - Terminó\n");
						exit(0);
					}
				}
				else {
					SignalHandler :: getInstance()->registrarHandler(SIGINT, &sigint_handler);
					log->writeToLogFile("Server P4 - Atendiendo peticiones de lista de salas de chat hasta recibir la señal SIGINT\n");
					while(sigint_handler.getGracefulQuit() == 0)
						servidor->atenderPeticionListaDeSalas();
					SignalHandler::destruir();
					log->writeToLogFile("Server P4 - Liberando memoria\n");
					delete(servidor);
					string tmp = "Server P4 - Enviando señal SIGINT a pid = ";
					char pid[8];
					sprintf(pid, "%d",getppid());
					tmp += pid;
					tmp += "\n";
					log->writeToLogFile(tmp);
					kill(getppid(), SIGINT);
					log->writeToLogFile("Server P4 - Terminó\n");
					exit(0);
				}
			}
			else {
				SignalHandler :: getInstance()->registrarHandler(SIGINT, &sigint_handler);
				log->writeToLogFile("Server P5 - Atendiendo peticiones de registro de nuevo cliente hasta recibir la señal SIGINT\n");
				while(sigint_handler.getGracefulQuit() == 0)
					servidor->atenderPeticionClienteNuevo();
				SignalHandler::destruir();
				log->writeToLogFile("Server P5 - Liberando memoria\n");
				delete(servidor);
				string tmp = "Server P5 - Enviando señal SIGINT a pid = ";
				char pid[8];
				sprintf(pid, "%d",getppid());
				tmp += pid;
				tmp += "\n";
				log->writeToLogFile(tmp);
				kill(getppid(), SIGINT);
				log->writeToLogFile("Server P5 - Terminó\n");
				exit(0);
			}
		}
		else {
			SignalHandler :: getInstance()->registrarHandler(SIGINT, &sigint_handler);
			log->writeToLogFile("Server P6 - Atendiendo peticiones de envío de mensaje hasta recibir la señal SIGINT\n");
			while(sigint_handler.getGracefulQuit() == 0)
				servidor->atenderPeticionMensajeNuevo();
			SignalHandler::destruir();
			log->writeToLogFile("Server P6 - Liberando memoria\n");
			delete(servidor);
			log->writeToLogFile("Server P6 - Terminó\n");
			exit(0);
		}

	} else {
		//cliente
		//Obtengo la instancia del Logger (le paso el flag de debug)
		log = Logger::Instance(debug);
		Cliente cliente("/bin/ls", 'a');
		char apodo[STRING_SIZE];
		cout << "Bienvenido a ConcuConversation" << endl;
		cout<<"Ingrese su apodo: ";
		log->writeToLogFile("Cliente P1 - Solicitando apodo y registrando nuevo usuario en el server\n");
		cin.getline(apodo, STRING_SIZE);
		while(!cliente.enviarPeticionClienteNuevo(apodo)) {
			cout<<"El apodo ingresado ya está en uso, ingrese otro: ";
			cin.getline(apodo, STRING_SIZE);
		}
		log->writeToLogFile("Cliente P1 - Nuevo usuario registrado en el server\n");
		log->writeToLogFile("Cliente P1 - Enviando peticion de lista de salas\n");
		vector<string> salas = cliente.enviarPeticionListaDeSalas();
		int nroSala = 0;
		if(salas.size()==0) {
			log->writeToLogFile("Cliente P1 - No hay salas disponibles\n");
			cout<<"Aun no se ha creado ninguna sala debe crear una sala nueva"<<endl;
		} else {
			log->writeToLogFile("Cliente P1 - Listando salas disponibles\n");
			cout<<"Las salas disponibles son: "<<endl;
			cout<<"0 - Crear sala nueva"<<endl;
			for(unsigned int i = 0; i < salas.size(); i++)
				cout<<i+1<<" - "<<salas[i]<<endl;
			cout<<"Ingrese el nro de sala: ";
			char aux[STRING_SIZE];
			cin.getline(aux, STRING_SIZE);
			nroSala = atoi(aux);
		}
		if(nroSala == 0) {
			log->writeToLogFile("Cliente P1 - Creando nueva sala de chat\n");
			char nombreSala[STRING_SIZE];
			cout<<"Ingrese el nombre de la sala nueva: ";
			cin.getline(nombreSala, STRING_SIZE);
			while(!cliente.enviarPeticionConectarASala(nroSala, nombreSala)) {
				cout<<"Ya existe una sala con ese nombre, ingrese otro: ";
				cin.getline(nombreSala, STRING_SIZE);
			}
			string tmp = "Cliente P1 - Se ha creado la sala ";
			tmp += nombreSala;
			tmp += "\n";
			log->writeToLogFile(tmp);
			cout<<"Se ha creado la sala "<<nombreSala<<endl;
			tmp = "Cliente P1 - Usuario conectado a sala ";
			tmp += nombreSala;
			tmp += "\n";
			log->writeToLogFile(tmp);
			cout<<"Estas conectado a la sala "<<nombreSala<<endl;
		}
		else {
			cliente.enviarPeticionConectarASala(nroSala, "");
			string tmp = "Cliente P1 - Usuario conectado a sala ";
			tmp += salas[nroSala-1];
			tmp += "\n";
			log->writeToLogFile(tmp);
			cout<<"Estas conectado a la sala "<<salas[nroSala-1]<<endl;
		}
		cout<<"Ingrese usuarios para obtener la lista de usuarios conectados"<<endl;
		cout<<"Ingrese salir para salir de la sala"<<endl;

		//Proceso 2: envío de mensajes
		if(fork()==0) {
			char mensaje[STRING_SIZE];
			while(strcmp(mensaje, "salir")!=0) {
				cin.getline(mensaje, STRING_SIZE);
				if(strcmp(mensaje, "usuarios")==0) {
					log->writeToLogFile("Cliente P2 - Enviando peticion de lista de usuarios conectados\n");
					vector<string> usuarios = cliente.enviarPeticionListaDeUsuarios();
					log->writeToLogFile("Cliente P2 - Listando usuarios conectados\n");
					cout<<"Los usuarios conectados son:"<<endl;
					for(unsigned int i = 0; i < usuarios.size(); i++)
						cout<<i + 1<<" - "<<usuarios[i]<<endl;
				} else {
					string tmp = "Cliente P2 - Enviando mensaje: ";
					tmp += mensaje;
					tmp += "\n";
					log->writeToLogFile(tmp);
					cliente.enviarMensaje(mensaje);
				}
			}
			string tmp = "Cliente P2 - Enviando señal SIGINT a pid = ";
			char pid[8];
			sprintf(pid, "%d",getppid());
			tmp += pid;
			tmp += "\n";
			log->writeToLogFile(tmp);
			kill(getppid(), SIGINT);
			log->writeToLogFile("Cliente P2 - Terminó\n");
			exit(0);
		}
		else {
			//Proceso 1: recepción de mensajes
			SIGINT_Handler sigint_handler;
			SignalHandler :: getInstance()->registrarHandler(SIGINT, &sigint_handler);
			log->writeToLogFile("Cliente P1 - Esperando nuevo mensaje\n");
			while(sigint_handler.getGracefulQuit() == 0)
			{
				string mensaje = cliente.recibirMensaje();
				if(sigint_handler.getGracefulQuit() == 0) {
					string tmp = "Cliente P1 - Mensaje recibido : ";
					tmp += mensaje;
					tmp += "\n";
					log->writeToLogFile(tmp);
					cout<<mensaje<<endl;
				}
			}
			SignalHandler::destruir();
			log->writeToLogFile("Cliente P1 - Terminó\n");
			exit(0);
		}
	}
	exit(0);
}

void usage() {
	cout << "Usage:" << endl;
	cout << "\t" << "tp2 [options]" << endl;
	cout << "Options:" << endl;
	cout << "\t" << "-h, --help" << "\t" << "Prints usage information." << endl;
	cout << "\t" << "-V, --version" << "\t" << "Prints version information." << endl;
	cout << "\t" << "-s, --server" << "\t" << "Launches the conversations server." << endl;
	cout << "\t" << "-d, --debug" << "\t" << "Debug mode." << endl;
	cout << "Examples:" << endl;
	cout << "\t" << "tp2 -d" << endl;
}

int parse_command_line(int argc, char **argv, bool *server, bool *debug) {
	char *cadena;

	for (int i = 1; i < argc; i++) {
		cadena = argv[i];
		if (cadena[0] == '-') {
			if (strcmp(cadena, "-d") == 0 || strcmp(cadena, "--debug") == 0) {
				*debug = true;
			} else if (strcmp(cadena, "-s") == 0 || strcmp(cadena, "--server") == 0) {
				*server = true;
			} else if (strcmp(cadena, "-h") == 0 || strcmp(cadena, "--help") == 0) {
				usage();
				return 1;
			} else if (strcmp(cadena, "-V") == 0 || strcmp(cadena, "--version") == 0) {
				cout << "TP2 - Version 1.0" << endl;
				cout << "Colinas, Sergio - 84189" << endl;
				return 1;
			} else {
				cerr << "tp2: " << " invalid option " << cadena << endl;
				cerr << "Try './tp2 -h' for more information" << endl;
				return 1;
			}
		} else {
			usage();
			return 1;
		}
	}

	return 0;

}
